Metona 内部 API 请求与响应标准

Metona Internal Representation (IR) —— 项目内所有 AI 交互的统一数据格式。无论底层对接 DeepSeek、Ollama、Agnes 还是其他 LLM,Agent Loop、UI、记忆系统均读写此标准格式。Provider Adapter 负责外部 API 与此 IR 之间的双向转换。

版本: v1.0.0 适用范围: Electron 主进程 / IPC / 渲染进程 更新日期: 2026-07-15
📋 文档层级:本文档是 类型系统与数据格式的权威定义,与《构建指南》第四章(ReAct)、第五章(Harness)对应。冲突时以本文档为准。

🎯 核心思想

Metona 面向多 LLM Provider,每个外部 API 的请求/响应格式各不相同(OpenAI 格式、Anthropic 格式、Ollama 原生格式等)。 如果项目各处代码直接依赖外部格式,切换 Provider 或新增模型将导致大规模改动。

Metona IR 在项目内部建立一道抽象边界:

┌──────────────────────────┐
│   Agent Loop / IPC / UI   │  ← 只读写 Metona IR
└────────────┬─────────────┘┌────────────▼─────────────┐
│     Metona IR Standard    │  ← 项目内唯一标准
└────────────┬─────────────┘
             │
    ┌────────┼────────┐
    │        │        │
┌───▼──┐ ┌──▼───┐ ┌─▼───┐
│DeepSeek│ │Agnes│ │Ollama│  ← Adapter 层
└───────┘ └─────┘ └──────┘

铁律electron/harness/ 下的所有代码、src/ 下的所有 UI 代码、IPC 通道传输的数据,只能使用 Metona IR 定义的类型。 任何外部 API 的原始类型不得穿透到这些层。


🏗️ 架构概览

一次完整的用户请求经过以下数据流转:

1. UI (Renderer)
   │  构造 MetonaRequest,通过 IPC 发送到主进程
   │
2. IPC Bridge
   │  传输 MetonaRequest JSON
   │
3. Context Builder
   │  注入 System Prompt、会话历史、检索记忆、可用工具列表
   │  输出 MetonaContext
   │
4. Provider Adapter
   │  将 MetonaContext 转换为目标 Provider 的原生请求格式
   │  调用外部 API
   │  将原生响应转换为 MetonaResponse / MetonaStreamEvent
   │
5. Agent Loop Engine
   │  按 ReAct 状态机解析 MetonaResponse
   │  提取 Thought → 执行 ToolCall → 收集 Observation
   │  构建下一轮的 MetonaRequest
   │
6. IPC → UI
   │  将 MetonaStreamEvent / MetonaResponse 推送回渲染进程

📤 请求标准:MetonaRequest

Agent Loop 向 Provider Adapter 发出的统一请求。每次 ReAct 迭代构造一个新的 MetonaRequest。

完整类型定义

// ====== electron/harness/types/metona-request.ts ======

export interface MetonaRequest {
  /** 请求元信息 */
  meta: MetonaRequestMeta;

  /** System Prompt(行为宪法) */
  systemPrompt: MetonaSystemPrompt;

  /** 消息列表(含历史 + 当前用户输入 + 工具结果) */
  messages: MetonaMessage[];

  /** 本轮可用的工具定义列表 */
  tools?: MetonaToolDef[];

  /** 生成参数 */
  params: MetonaGenerationParams;

  /** 安全约束 */
  constraints?: MetonaConstraints;
}

export interface MetonaRequestMeta {
  sessionId: string;            // 会话 ID
  iteration: number;            // 当前 ReAct 迭代轮次(从 1 开始)
  requestId: string;            // 本次请求的唯一 ID
  timestamp: number;            // Unix 毫秒时间戳
  agentVersion: string;         // Agent 引擎版本
}

export interface MetonaSystemPrompt {
  /** 角色定义(静态区,利用 LLM 缓存) */
  roleDefinition: string;

  /** 输出格式约束 */
  outputConstraints: string;

  /** 安全准则 */
  safetyGuidelines: string;

  /** 动态注入的尾部提醒 */
  dynamicReminders?: string;
}

export interface MetonaGenerationParams {
  maxTokens?: number;           // 最大生成 token 数
  temperature?: number;        // 温度(默认 0.0,Agent 需要确定性)
  topP?: number;               // 核采样
  stream?: boolean;            // 是否流式输出
  stopSequences?: string[];   // 停止序列
  thinkingEnabled?: boolean;  // 是否启用思考模式
  thinkingEffort?: 'low' | 'medium' | 'high' | 'max';  // 思考强度(替代 thinkingBudget,各 Provider 映射见 Adapter 规范)
}

export interface MetonaConstraints {
  allowedTools?: string[];     // 本迭代允许使用的工具白名单
  maxToolCalls?: number;      // 单轮最大工具调用数
  timeoutMs?: number;         // 本请求整体超时
}

字段说明

字段类型必填说明
metaMetonaRequestMeta必填请求元信息,含 sessionId、iteration、requestId、timestamp
systemPromptMetonaSystemPrompt必填分区化的 System Prompt,Adaper 负责拼接为 Provider 格式
messagesMetonaMessage[]必填统一消息列表,见下方消息格式
toolsMetonaToolDef[]可选本轮可用工具列表
paramsMetonaGenerationParams必填生成参数
constraintsMetonaConstraints可选安全约束和限流参数

💬 消息格式:MetonaMessage

Metona 统一消息结构。所有角色(system / user / assistant / tool)共用同一结构,通过 role 区分。

export interface MetonaMessage {
  role: 'system' | 'user' | 'assistant' | 'tool';

  /** 文本内容(纯文本或 Markdown) */
  content: string;

  /** (仅 assistant)思考/推理内容 */
  reasoningContent?: string;

  /** (仅 assistant)工具调用请求 */
  toolCalls?: MetonaToolCall[];

  /** (仅 tool)工具执行结果 */
  toolResult?: MetonaToolResult;

  /** 时间戳 */
  timestamp: number;

  /** 所属迭代轮次 */
  iteration?: number;

  /** 图片内容(可选,用于多模态) */
  images?: MetonaImageContent[];
}

export interface MetonaImageContent {
  url: string;              // 图片公网 URL 或 base64 data URI
  detail?: 'low' | 'high' | 'auto';
}
💡 设计要点:部分 LLM 的 reasoning_content 需要伴随 toolCalls 回传上下文。 MetonaMessage 统一携带 reasoningContent,由 Adapter 决定是否需要回传。
🖼 多模态消息转换:MetonaMessage.content(string)+ images(数组)的分离设计比 OpenAI 的 content 数组更清晰。Adapter 负责转换:
DeepSeek/Agnes (OpenAI 兼容)content 数组 = [{type:"text", text: content}, ...images.map(i => ({type:"image_url", image_url:{url: i.url}}))]
Ollamacontent 保持 string,images 作为消息的独立字段传入 base64 数组(Adapter 需将 URL 下载为 base64)
纯文本消息(无 images):所有 Provider 的 content 直接传 string

🔧 工具定义:MetonaToolDef

统一的工具描述格式。内置工具和 MCP 动态工具都使用此结构。

export interface MetonaToolDef {
  name: string;                // 工具唯一名称
  description: string;         // 功能描述(供 LLM 阅读)
  parameters: MetonaToolParams; // 参数 JSON Schema
  category: MetonaToolCategory; // 分类
  riskLevel: MetonaRiskLevel;  // 风险等级
  requiresPermission: boolean; // 是否需要用户授权
  timeoutMs: number;          // 超时时间
}

export interface MetonaToolParams {
  type: 'object';
  properties: Record<string, MetonaParamField>;
  required?: string[];
}

export interface MetonaParamField {
  type: 'string' | 'number' | 'boolean' | 'object' | 'array';
  description: string;
  enum?: string[];
  items?: MetonaParamField;
}

export enum MetonaToolCategory {
  FILESYSTEM = 'filesystem',
  SEARCH = 'search',
  CALCULATION = 'calculation',
  CODE_EXECUTION = 'code_execution',
  NETWORK = 'network',
  DATABASE = 'database',
  MCP = 'mcp',
  CUSTOM = 'custom',
}

export enum MetonaRiskLevel {
  SAFE = 'safe',
  LOW = 'low',
  MEDIUM = 'medium',
  HIGH = 'high',
  CRITICAL = 'critical',
}

MetonaToolDef 与内部 ToolDefinition 的关系

项目内部存在两种工具类型:MetonaToolDef(IR 标准类型)和 ToolDefinition(内部实现类型,使用 Zod Schema 进行运行时参数校验)。两者的关系如下:

维度MetonaToolDef(IR 标准)ToolDefinition(内部实现)
用途跨进程传输、LLM 可读描述、IPC 通信运行时参数校验、工具注册、安全检查
参数格式JSON Schema(MetonaToolParamsZod Schema(支持类型推断和运行时校验)
定义位置本文档(IR 标准)electron/harness/tools/base-tool.ts
🔗 转换规则:BaseTool.getDescriptionForLLM() 负责将 Zod Schema → JSON Schema → MetonaToolDef
ToolDefinition.nameMetonaToolDef.name
ToolDefinition.parameters(Zod)→ MetonaToolDef.parameters(JSON Schema),使用 zod-to-json-schema 库转换
ToolDefinition.categoryMetonaToolDef.category(枚举值一致)
ToolDefinition.riskLevelMetonaToolDef.riskLevel(枚举值一致)
• IPC 通道和 Agent Loop 只使用 MetonaToolDef,不接触 ToolDefinition/Zod
• 工具注册表(ToolRegistry)内部使用 IBaseTool,对外暴露时转换为 MetonaToolDef
⚠️ 强制规则:架构文档中 9 个基础工具的参数定义必须以 MetonaToolDef 格式为准(JSON Schema),不再使用自然语言描述。内部实现时使用 Zod Schema 做运行时校验,通过 zod-to-json-schema 转换后对外暴露。

📥 响应标准:MetonaResponse

Provider Adapter 将外部 API 的原始响应转换为 MetonaResponse 后返回给 Agent Loop。

export interface MetonaResponse {
  /** 响应元信息 */
  meta: MetonaResponseMeta;

  /** 模型输出(完整文本) */
  content: string;

  /** 思考/推理内容(Thinking 模式) */
  reasoningContent?: string;

  /** 结构化输出(如果模型原生支持 JSON Schema) */
  structuredOutput?: unknown;

  /** 工具调用请求列表 */
  toolCalls?: MetonaToolCall[];

  /** Token 使用统计 */
  usage: MetonaTokenUsage;

  /** 停止原因 */
  finishReason: MetonaFinishReason;

  /** 错误信息(如果出错) */
  error?: MetonaError;
}

export interface MetonaResponseMeta {
  requestId: string;          // 对应的请求 ID
  provider: string;           // Provider 标识(如 'deepseek')
  model: string;              // 实际使用的模型名称
  latencyMs: number;          // 端到端延迟
  timestamp: number;          // 响应时间戳

  /** Provider 原生性能统计(可选,主要用于 Ollama) */
  perfStats?: {
    loadDurationMs?: number;      // 模型加载耗时
    promptEvalDurationMs?: number; // Prompt 评估耗时
    evalDurationMs?: number;       // 生成耗时
    tokensPerSecond?: number;     // 生成速率
  };
}

export interface MetonaTokenUsage {
  inputTokens: number;
  outputTokens: number;
  totalTokens: number;
  reasoningTokens?: number;  // Thinking 模式专用
  cacheHitTokens?: number;
  cacheMissTokens?: number;
}

export enum MetonaFinishReason {
  STOP = 'stop',                 // 自然结束
  LENGTH = 'length',             // 达到长度上限
  TOOL_CALLS = 'tool_calls',     // 因工具调用而停止
  CONTENT_FILTER = 'content_filter', // 内容过滤
  ERROR = 'error',               // 错误终止
}

⚡ 流式响应:MetonaStreamEvent

流式输出使用统一的事件类型,Agent Loop 和 UI 均可订阅。

/** 流式事件类型枚举 */
export enum MetonaStreamEventType {
  TEXT_DELTA = 'text_delta',           // 文本增量
  REASONING_DELTA = 'reasoning_delta', // 推理内容增量
  TOOL_CALL_DELTA = 'tool_call_delta', // 工具调用增量
  TOOL_CALL_COMPLETE = 'tool_call_complete',
  THINKING_START = 'thinking_start',     // 思考开始
  THINKING_END = 'thinking_end',         // 思考结束
  ERROR = 'error',                     // 流中错误
  DONE = 'done',                       // 流结束
  USAGE = 'usage',                     // Token 统计(通常在 DONE 前)
}

export interface MetonaStreamEvent {
  type: MetonaStreamEventType;
  requestId: string;
  sessionId: string;
  iteration: number;
  seq: number;                    // 序列号
  timestamp: number;

  /** 根据 type 使用不同字段 */
  delta?: string;                // TEXT_DELTA / REASONING_DELTA
  toolCallDelta?: {
    index: number;            // 工具调用索引(同一轮可能有多个)
    name?: string;            // 工具名称片段(首个事件携带)
    argsDelta?: string;       // 参数 JSON 增量片段
  };                             // TOOL_CALL_DELTA
  toolCall?: MetonaToolCall;    // TOOL_CALL_COMPLETE(拼接完成后的完整调用)
  usage?: MetonaTokenUsage;    // USAGE
  error?: MetonaError;         // ERROR
}

流式传输协议

IPC 通道使用 SSE-like 格式(Server-Sent Events),每条事件为一行 JSON:

// 实际传输格式(IPC 通道内,每行一条事件)
{"type":"thinking_start","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":0,"timestamp":1719000000000}
{"type":"reasoning_delta","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":1,"timestamp":1719000000100,"delta":"让我先分析问题的关键点..."}
{"type":"thinking_end","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":2,"timestamp":1719000000500}
{"type":"text_delta","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":3,"timestamp":1719000000600,"delta":"根据分析,"}
{"type":"text_delta","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":4,"timestamp":1719000000650,"delta":"答案是..."}
{"type":"tool_call_complete","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":5,"timestamp":1719000000700,"toolCall":{...}}
{"type":"usage","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":6,"timestamp":1719000000800,"usage":{"inputTokens":120,"outputTokens":45,"totalTokens":165}}
{"type":"done","requestId":"r_1","sessionId":"s_1","iteration":1,"seq":7,"timestamp":1719000000800}

流式工具调用拼接策略

不同 Provider 的流式工具调用返回方式不同,Adapter 负责统一处理:

Provider流式工具调用格式Adapter 处理方式
DeepSeek / Agnes 分片返回:delta.tool_calls[i] 先返回 index + function.name 片段,再返回 function.arguments 片段 Adapter 缓冲拼接:每个 delta.tool_calls 片段转换为 TOOL_CALL_DELTA 事件;拼接完成后发 TOOL_CALL_COMPLETE
Ollama 整块返回:message.tool_calls 在最后一个 chunk 中一次性返回 Adapter 直接转换为 TOOL_CALL_COMPLETE 事件(无 TOOL_CALL_DELTA
💡 拼接规则:Adapter 维护一个 Map<number, {name: string, argsBuffer: string}> 缓冲区。
• 收到 TOOL_CALL_DELTA 时:如果 name 不为空,初始化缓冲区;将 argsDelta 追加到 argsBuffer
• 收到流结束(done)或 finish_reason: "tool_calls" 时:遍历缓冲区,对每个拼接完整的工具调用发 TOOL_CALL_COMPLETE 事件(JSON.parse(argsBuffer) 作为 args
• UI 端可以选择忽略 TOOL_CALL_DELTA 事件,只监听 TOOL_CALL_COMPLETE(简化实现)

🧠 思考内容:MetonaThinking

各 Provider 的思考/推理内容格式不同(DeepSeek 用 reasoning_content、Anthropic 用 thinking block、Ollama 在 think 标签内),统一为 MetonaThinking。

export interface MetonaThinking {
  /** 思考内容文本 */
  content: string;

  /** 思考状态 */
  status: 'thinking' | 'complete';

  /** 思考耗时 (ms) */
  durationMs: number;

  /** 思考消耗的 token 数 */
  tokensUsed: number;
}
💡 流式思考:流式输出时,通过 thinking_start 事件开始,一系列 reasoning_delta 传输增量,最后 thinking_end 结束。 最终在 MetonaResponse 中合并为完整的 reasoningContent 字符串。

🔌 工具调用:MetonaToolCall

export interface MetonaToolCall {
  id: string;                      // 工具调用唯一 ID
  name: string;                    // 工具名称
  args: Record<string, unknown>; // 调用参数
  iteration: number;              // 所属 ReAct 迭代
  timestamp: number;
}

export interface MetonaToolResult {
  toolCallId: string;
  toolName: string;
  result: unknown;               // 工具原始返回值
  summary?: string;              // 人工可读摘要(用于 LLM 上下文注入)
  success: boolean;
  error?: string;
  durationMs: number;
  timestamp: number;
}

工具结果注入规则:工具执行完毕后,构造 role='tool' 的 MetonaMessage 追加到 messages 数组中。 summary 字段供 LLM 阅读(精简后),result 保留原始值供审计和调试。

🔧 Tool Calling 模式(首选):当使用 Tool Calling 时,Provider Adapter 在 MetonaRequest.tools 中传入工具列表,LLM 原生返回 tool_calls 结构化数据。Adapter 直接映射为 MetonaResponse.toolCalls无需文本解析
DeepSeek:请求参数 tools + tool_choice,响应 choices[0].message.tool_calls
Agnes:同 DeepSeek(OpenAI 兼容)
Ollama:请求参数 tools,响应 message.tool_calls
三个 Provider 均原生支持 Tool Calling,正则解析仅作为 Provider 不支持时的降级方案。
🧠 解析策略优先级:
1. Tool Calling(首选):利用 Provider 原生的 tools + tool_choice 参数,LLM 直接返回结构化 tool_calls
2. Structured Output(备选):当不需要工具调用但需要结构化输出时,使用 response_format: json_object
3. 正则降级(仅兜底):仅在 Provider 不支持 Tool Calling 时使用(当前三个 Provider 全部支持,实际不触发)。

❌ 错误格式:MetonaError

export interface MetonaError {
  code: MetonaErrorCode;          // 错误码
  message: string;               // 人类可读的错误描述
  provider?: string;              // 出错的 Provider
  providerCode?: string;         // Provider 原始错误码
  retryable: boolean;            // 是否可重试
  retryAfterMs?: number;        // 建议重试等待时间
}

export enum MetonaErrorCode {
  // 网络层
  NETWORK_TIMEOUT = 'network_timeout',
  NETWORK_ERROR = 'network_error',

  // 认证层
  AUTH_INVALID = 'auth_invalid',
  AUTH_EXPIRED = 'auth_expired',

  // 频率限制
  RATE_LIMITED = 'rate_limited',
  QUOTA_EXCEEDED = 'quota_exceeded',

  // 模型层
  MODEL_OVERLOADED = 'model_overloaded',
  MODEL_NOT_FOUND = 'model_not_found',
  CONTEXT_LENGTH_EXCEEDED = 'context_length_exceeded',
  OUTPUT_LENGTH_EXCEEDED = 'output_length_exceeded',

  // 内容层
  CONTENT_FILTERED = 'content_filtered',

  // 解析层
  PARSE_ERROR = 'parse_error',
  INVALID_RESPONSE = 'invalid_response',

  // Agent 层
  MAX_ITERATIONS = 'max_iterations',
  USER_ABORTED = 'user_aborted',
  TIMEOUT = 'timeout',
  UNKNOWN = 'unknown',
}

📋 上下文标准:MetonaContext

Context Builder 的输出,包含了完整的上下文信息,供 Agent Loop 和 Adapter 使用。

export interface MetonaContext {
  /** 上下文唯一标识 */
  id: string;

  /** 关联的会话 */
  sessionId: string;

  /** System Prompt 分区 */
  systemPrompt: MetonaSystemPrompt;

  /** 会话历史(最近 N 轮) */
  history: MetonaMessage[];

  /** 检索到的相关记忆 */
  relevantMemories: MetonaMemoryItem[];

  /** 当前任务信息 */
  currentTask: {
    userInput: string;
    iteration: number;
    taskGoal?: string;
  };

  /** 可用工具列表 */
  availableTools: MetonaToolDef[];

  /** 预估 Token 数 */
  estimatedTokens: number;

  /** 上下文使用率(estimatedTokens / contextWindow) */
  usageRatio: number;

  /** 是否需要压缩 */
  needsCompression: boolean;
}
💡 上下文压缩策略(Context Compression):
usageRatio > 0.8needsCompression = true,Agent Loop 进入 COMPRESSING 状态。
压缩流程(由 ContextBuilder 负责):
1. 保留最近 N 轮对话原文(N 由配置决定,默认 5)
2. 将更早的对话轮次用 LLM 摘要为一组 "对话摘要" 消息插入上下文
3. 保留所有 tool_callstool_results 的精简版(只保留工具名 + 结果状态,省略完整输出)
4. 保留 System Prompt 和 MEMORY.md 注入内容不变
5. 压缩后重新计算 estimatedTokens,确保 usageRatio < 0.5
注意区分:上下文压缩(压缩 LLM 对话窗口)≠ 记忆压缩(清理 SQLite 记忆库),两者由不同组件负责。

🧩 记忆格式:MetonaMemoryItem

export interface MetonaMemoryItem {
  id: string;
  type: 'episodic' | 'semantic' | 'working';

  /** 可被 LLM 阅读的记忆内容 */
  content: string;

  /** 精简摘要(上下文紧张时使用) */
  summary?: string;

  /** 来源 */
  source: 'user_input' | 'tool_result' | 'agent_thought' | 'imported';

  /** 重要程度 0-1 */
  importance: number;

  /** 检索相关性分数(仅在检索结果中出现) */
  relevanceScore?: number;

  sessionId?: string;
  createdAt: number;
  expiresAt?: number;
}

🔗 Provider Adapter 规范

每个 LLM Provider 必须实现一个 Adapter,负责 Metona IR 和外部 API 格式之间的双向转换。

export interface IMetonaProviderAdapter {
  /** Provider 标识 */
  readonly providerId: string;

  /** 支持的模型列表 */
  readonly supportedModels: string[];

  /** 上下文窗口大小 */
  getContextWindow(model: string): number;

  /** 健康检查 */
  healthCheck(): Promise<boolean>;

  /**
   * 核心方法:发送请求
   * @param request - Metona 标准请求
   * @returns Metona 标准响应
   */
  send(request: MetonaRequest): Promise<MetonaResponse>;

  /**
   * 核心方法:发送流式请求
   * @param request - Metona 标准请求
   * @param onEvent - 流式事件回调
   * @returns 完整响应(流结束后返回)
   */
  sendStream(
    request: MetonaRequest,
    onEvent: (event: MetonaStreamEvent) => void
  ): Promise<MetonaResponse>;

  /** 获取可用模型列表 */
  listModels(): Promise<MetonaModelInfo[]>;
}

export interface MetonaModelInfo {
  id: string;
  providerId: string;
  displayName: string;
  contextWindow: number;
  maxOutput: number;
  supportsStreaming: boolean;
  supportsThinking: boolean;
  supportsImages: boolean;
  supportsToolCalling: boolean;
  supportsStructuredOutput: boolean;
  pricing?: MetonaPricing;
}

export interface MetonaPricing {
  inputPerMillion: number;
  outputPerMillion: number;
  currency: string;
}

Adapter 实现清单

适配器providerId目标 API传输格式
DeepSeekAdapter deepseek https://api.deepseek.com/chat/completions OpenAI 兼容 JSON
AgnesAdapter agnes-ai https://apihub.agnes-ai.com/v1/chat/completions OpenAI 兼容 JSON
OllamaAdapter ollama http://localhost:11434/api/chat Ollama 原生 JSON / NDJSON
AnthropicAdapter(未来计划,未实现) anthropic https://api.anthropic.com/v1/messages Anthropic 原生 JSON
OpenAIAdapter(未来计划,未实现) openai https://api.openai.com/v1/chat/completions OpenAI 原生 JSON

Thinking 模式 Provider 映射表

各 Provider 的思考模式控制方式不同,Adapter 负责将 MetonaGenerationParams.thinkingEffort 映射为目标 Provider 的原生参数:

Provider开启方式thinkingEffort 映射回传规则
DeepSeek thinking: {type: "enabled"} + reasoning_effort low/medium → "high", high → "high", max → "max" 工具调用轮次的 reasoning_content 必须回传上下文
Agnes (OpenAI 兼容) chat_template_kwargs: {enable_thinking: true} low → false, medium/high/max → true 不强制回传
Agnes (Anthropic 兼容) thinking: {type: "enabled", budget_tokens: N} low → 1024, medium → 2048, high → 4096, max → 8192 不强制回传
Ollama think: true/false"high"/"medium"/"low" low → "low", medium → "medium", high → "high", max → true message.thinking 字段,不强制回传
💡 思考内容字段映射:
• DeepSeek/Agnes(OpenAI): reasoning_contentMetonaResponse.reasoningContent
• Agnes(Anthropic): thinking block → MetonaResponse.reasoningContent
• Ollama: message.thinkingMetonaResponse.reasoningContent
流式模式下,思考内容增量统一映射为 reasoning_delta 事件。

MVP 优先级

当前优先实现以下 3 个 Adapter,Anthropic 和 OpenAI 为未来计划:

优先级Adapter状态
P0DeepSeekAdapterMVP 必须实现
P0AgnesAdapterMVP 必须实现
P1OllamaAdapterMVP 必须实现(本地推理)
P2AnthropicAdapter未来计划
P2OpenAIAdapter未来计划

Provider 故障转移策略

当主 Provider 不可用时,Agent Loop 按以下策略处理:

步骤条件动作
1. 重试MetonaError.retryable === trueretryAfterMs 等待后重试(最多 3 次)
2. 故障转移重试仍失败 + 已配置 fallback Provider切换到备选 Provider 重新发送请求
3. 通知用户故障转移触发时通过 IPC 推送 agent:providerSwitched 事件,UI 显示 Toast 提示
4. 报错无 fallback 或 fallback 也失败返回 MetonaError,UI 显示错误 + 重试按钮
💡 配置项:app_config 表中增加以下配置:
llm.fallbackProvider(string,备选 Provider ID)
llm.fallbackModel(string,备选模型名)
llm.fallbackApiKey(string,备选 API Key,加密存储)
用户可在设置界面配置备选 Provider。未配置时跳过故障转移步骤。

📖 完整示例

示例 1:普通文本对话(非流式)

用户发送问题,Agent Loop 构造请求,获得非流式回答。

// ===== Agent Loop 构造 =====
const request: MetonaRequest = {
  meta: { sessionId: 's_abc', iteration: 1, requestId: 'r_001', timestamp: Date.now(), agentVersion: '1.0.0' },
  systemPrompt: {
    roleDefinition: '你是一个专业的编程助手。',
    outputConstraints: '用中文回答,代码块使用 ``` 包裹。',
    safetyGuidelines: '不编造事实,不确定时如实说明。',
  },
  messages: [
    { role: 'user', content: '解释 JavaScript 的事件循环机制。', timestamp: Date.now() },
  ],
  params: { maxTokens: 4096, temperature: 0.0, stream: false, thinkingEnabled: false },
};

// ===== Adapter 返回 =====
const response: MetonaResponse = {
  meta: { requestId: 'r_001', provider: 'deepseek', model: 'deepseek-v4-pro', latencyMs: 850, timestamp: Date.now() },
  content: 'JavaScript 的事件循环(Event Loop)是...\\n\\n```js\\nconsole.log(1)...\\n```',
  usage: { inputTokens: 120, outputTokens: 350, totalTokens: 470 },
  finishReason: MetonaFinishReason.STOP,
};

示例 2:ReAct 工具调用(流式)

用户请求需要工具调用,Agent Loop 迭代两次。

// ===== 第 1 轮:Agent 发送请求,LLM 返回工具调用(流式) =====
const request1: MetonaRequest = {
  meta: { sessionId: 's_xyz', iteration: 1, requestId: 'r_002', timestamp: Date.now(), agentVersion: '1.0.0' },
  systemPrompt: { /* ... */ },
  messages: [
    { role: 'user', content: '读取 /home/user/data.csv 并分析内容。', timestamp: Date.now() },
  ],
  tools: [
    { name: 'read_file', description: '读取文件内容', /* ... */ },
    { name: 'analyze_csv', description: '分析 CSV 数据', /* ... */ },
  ],
  params: { stream: true, thinkingEnabled: true },
};

// 流式事件:
// → thinking_start → reasoning_delta ... → thinking_end
// → tool_call_complete { id: 'tc_1', name: 'read_file', args: { file_path: '/home/user/data.csv' } }
// → done

// ===== Agent Loop 执行工具后,构造第 2 轮请求 =====
const request2: MetonaRequest = {
  meta: { /* ... */, iteration: 2, requestId: 'r_003' },
  // 消息包含历史 + 工具结果
  messages: [
    { role: 'user', content: '读取 /home/user/data.csv 并分析内容。', timestamp: t },
    { role: 'assistant', content: '', toolCalls: [{ id: 'tc_1', name: 'read_file', args: { file_path: '/home/user/data.csv' }, iteration: 1 }], timestamp: t },
    {
      role: 'tool',
      content: '',
      toolResult: { toolCallId: 'tc_1', toolName: 'read_file', result: '...', success: true, durationMs: 5, timestamp: t,
        summary: '文件 data.csv 包含 1000 行数据,字段为 name,age,email,city' },
      timestamp: t
    },
  ],
  tools: [/* 同上 */],
  params: { stream: true, thinkingEnabled: false },
};

🔄 迁移指南

将现有代码迁移到 Metona IR 标准的步骤。

文件结构

electron/harness/types/
├── metona-request.ts      // MetonaRequest, MetonaMessage, MetonaToolDef 等
├── metona-response.ts     // MetonaResponse, MetonaStreamEvent, MetonaError 等
├── metona-context.ts      // MetonaContext, MetonaSystemPrompt
├── metona-memory.ts       // MetonaMemoryItem
├── metona-tool.ts         // MetonaToolCall, MetonaToolResult
├── metona-adapter.ts      // IMetonaProviderAdapter 接口
└── index.ts               // 统一导出

electron/harness/adapters/
├── base-adapter.ts        // Adapter 基类(共享逻辑)
├── deepseek.adapter.ts
├── agnes-ai.adapter.ts
├── ollama.adapter.ts
# ├── anthropic.adapter.ts    # 未来计划
# └── openai.adapter.ts       # 未来计划

迁移检查清单

#检查项涉及文件
1Agent Loop Engine 只引用 MetonaRequest / MetonaResponseagent-loop/engine.ts
2Context Builder 输出 MetonaContextharness/prompts/
3Tool Registry 使用 MetonaToolDef / MetonaToolCall / MetonaToolResultharness/tools/
4Memory Manager 使用 MetonaMemoryItemharness/memory/
5IPC Preload 桥接层只传递 Metona IR 类型electron/preload.ts
6IPC Handlers 的输入输出为 Metona IR 类型electron/ipc/*.handlers.ts
7React 组件/Zustand Store 只读写 Metona IR 类型src/stores/, src/components/
8每个 Provider Adapter 实现 IMetonaProviderAdapterharness/adapters/
9流式事件通过 MetonaStreamEvent 推送hooks/useAgentStream.ts
10所有外部 API 原生类型不出现在 harness/ 和 src/ 中全局
⚠️ 强制规则:违反第 10 条的代码不得合入主分支。Code Review 时以此标准为基准。

📜 Metona 内部 API 请求与响应标准 —— 项目端到端类型安全的基础

版本: v1.0.0 · 生效日期: 2026-07-15

所属项目: Metona (AI Agent Desktop) · 技术栈: TypeScript + React + SQLite + Electron